﻿using CommonEntity.CommonImageEntities;
using itk.simple;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using ImageDomain.DE;
using Point3d = CommonEntity.CommonImageEntities.Point3d;

namespace ImageDomain.Utils;
public class ImageHandler
{

    #region Singleton 懒加载单例模式
    private ImageHandler() { }

    private static readonly Lazy<ImageHandler> Lazy = new(() => new ImageHandler());

    public static ImageHandler Instance => Lazy.Value;

    #endregion

    /// <summary>
    /// 
    /// </summary>
    /// <param name="itkImage2d"></param>
    /// <param name="winAdjust"></param>
    /// <returns></returns>
    public Mat? GetMatWithInt32(Image itkImage2d, WindowAdjust winAdjust)
    {
        var filter = new IntensityWindowingImageFilter();
        filter.SetOutputMinimum(winAdjust.OutputMin);
        filter.SetOutputMaximum(winAdjust.OutputMax);
        filter.SetWindowMinimum(winAdjust.WindowMin);
        filter.SetWindowMaximum(winAdjust.WindowMax);

        var image = filter.Execute(itkImage2d);
        var buffer = image.GetBufferAsInt32();
        var mat = Mat.FromPixelData(new[] { (int)itkImage2d.GetHeight(), (int)itkImage2d.GetWidth() }, MatType.CV_32SC1,
            buffer);
        mat.ConvertTo(mat, MatType.CV_8UC4);
        return mat;
    }

    /// <summary>
    /// 特殊定制
    /// </summary>
    /// <param name="itkImage2d"></param>
    /// <param name="winAdjust"></param>
    /// <returns></returns>
    public Mat? GetMatWithInt8(Image itkImage2d, WindowAdjust winAdjust)
    {
        var filter = new IntensityWindowingImageFilter();
        filter.SetOutputMinimum(0);
        filter.SetOutputMaximum(3);
        filter.SetWindowMinimum(0);
        filter.SetWindowMaximum(3);

        var image = filter.Execute(itkImage2d);
        var buffer = image.GetBufferAsInt8();
        var mat = Mat.FromPixelData(new[] { (int)itkImage2d.GetHeight(), (int)itkImage2d.GetWidth() }, MatType.CV_8SC1,
            buffer);
        mat.ConvertTo(mat, MatType.CV_8UC1);
        return mat;
    }

    /// <summary>
    /// 获取像素点
    /// </summary>
    /// <param name="itkImage"></param>
    /// <param name="point"></param>
    /// <returns></returns>
    public int GetPixel(IImage3d image3d, Point3d point)
    {
        var vector = new VectorUInt32();
        vector.Add((uint)point.X);
        vector.Add((uint)point.Y);
        vector.Add((uint)point.Z);
        var val = image3d.ItkImage!.GetPixelAsInt32(vector);
        return val;
    }

    public Image GetAxSliceImage(Image itkImage, int index, uint[] size)
    {
        return SimpleITK.Extract(
            itkImage,
            new VectorUInt32() { size[0], size[1], 0 },
            new VectorInt32(new[] { 0, 0, index }));
    }

    public Image GetSagSliceImage(Image itkImage, int index, uint[] size)
    {
        return SimpleITK.Extract(
            itkImage,
            new VectorUInt32() { 0, size[1], size[2] },
            new VectorInt32(new[] { index, 0, 0 }));
    }

    public Image GetCorSliceImage(Image itkImage, int index, uint[] size)
    {
        return SimpleITK.Extract(
            itkImage,
            new VectorUInt32() { size[0], 0, size[2] },
            new VectorInt32(new[] { 0, index, 0 }));
    }

    public int[] GetMinMaxVal(Image itkImage)
    {
        using var filter = new StatisticsImageFilter();
        filter.Execute(itkImage);
        var min = (int)filter.GetMinimum();
        var max = (int)filter.GetMaximum();
        return new [] { min, max };
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="cvMats"></param>
    /// <param name="spacing"></param>
    /// <param name="origin"></param>
    /// <param name="direction"></param>
    /// <returns></returns>
    public Image ConvertCvMaskMatsToImage(IList<Mat> cvMats, IReadOnlyList<double> spacing, IReadOnlyList<double> origin, IReadOnlyList<double> direction)
    {
        var width = cvMats[0].Width;
        var height = cvMats[0].Height;
        var count = cvMats.Count;

        var imageData = new byte[width * height * count];
        for (var i = 0; i < count; i++)
        {
            var mat = cvMats[i];
            if (mat.Channels() > 1)
                mat = mat.CvtColor(ColorConversionCodes.BGRA2GRAY);
            Marshal.Copy(mat.Data, imageData, i * width * height, width * height);
        }

        var itkImage = new Image((uint)width, (uint)height, (uint)count, PixelIDValueEnum.sitkUInt8);
        itkImage.SetSpacing(new VectorDouble(spacing));
        itkImage.SetOrigin(new VectorDouble(origin));
        itkImage.SetDirection(new VectorDouble(direction));
        var ptr = itkImage.GetBufferAsVoid();
        Marshal.Copy(imageData, 0, ptr, imageData.Length);
        return SimpleITK.Cast(itkImage, PixelIDValueEnum.sitkInt32);
    }


    public Image CreateMaskImage(IImage3d image3d)
    {
        var mask = new Image(new VectorUInt32() { image3d.OriginSize![0], image3d.OriginSize![1], 
            image3d.OriginSize![2] }, PixelIDValueEnum.sitkInt8);

        mask.SetDirection(new VectorDouble()
        {
            image3d.Direction![0], image3d.Direction![1], image3d.Direction![2],
            image3d.Direction![3], image3d.Direction![4], image3d.Direction![5],
            image3d.Direction![6], image3d.Direction![7], image3d.Direction![8]
        });

        mask.SetOrigin(new VectorDouble()
        {
            image3d.Origin![0], image3d.Origin[1], image3d.Origin[2]
        });

        mask.SetSpacing(new VectorDouble() { image3d.Spacing![0], image3d.Spacing![1], image3d.Spacing![2] });

        return mask;
    }

}
